package com.yricky.astral.compile
import com.yricky.astral.lib.VerilogKeyword
import com.yricky.astral.structure.INetList
import com.yricky.astral.structure.AstralModule

fun INetList.generate(attachment:String = ""):String{
    val gIO = generateIOPort()
    val gLo = generateBlocks()
    return "${generateHeader()}\n" +
            "${VerilogKeyword.MODULE} ${name}${gIO};\n" +
            gLo +
            attachment + "\n" +
            VerilogKeyword.ENDMODULE
}


/**
 * Generate verilog code.
 */
fun AstralModule.generate(): String {
    return genNetList().generate(
        attachment = attachments
            .map { it() }
            .ifEmpty { listOf("") }
            .reduce{ a,b-> "$a\n$b" }
    )
}


fun INetList.generateIOPort():String{
    val sb = StringBuilder()
    if(iPorts.isEmpty() and oPorts.isEmpty())
        return ""
    sb.append("(\n")
    iPorts.keys.forEachIndexed { _, s ->
        sb.append("  ")
        sb.append(iPorts[s]?.generateDeclareIO())
        sb.append(",\n")
    }

    oPorts.keys.forEachIndexed { index, s ->
        sb.append("  ")
        sb.append(oPorts[s]?.generateDeclareIO())
        sb.append(if(index == oPorts.size-1) "\n)" else ",\n")
    }
    return sb.toString()
}

fun INetList.generateBlocks():String{
    val sb = StringBuilder()
    netNodes.forEach{
        if((!iPorts.contains(it.key)) and (!oPorts.contains(it.key)))
            sb.append(it.value.generateDeclare())
        sb.append(it.value.generateLogic())
    }
    return sb.toString()
}

fun INetList.generateHeader():String = "/*\n" +
        " * Module $name is generated by Astral.\n" +
        " * Astral is designed by Yricky.\n" +
        " */\n"